//
// Created by RandBII on 2020/12/30.
//


#include "VideoGLRender.h"

//静态变量初始化一下
VideoGLRender *VideoGLRender::mInstance = nullptr;
std::mutex VideoGLRender::mMutex;


static char vShaderStr[] = {
        "#version  300es\n"
        "layout(location = 0 ) in vec4 a_position;\n"
        "layout(location = 1) in vec2 a_texCoord;\n"
        "uniform mat4 u_MVPMatrix;\n"
        "out vec2 v_texCoord;\n"
        "void mian()\n"
        "{\n"
        "gl_Position = u_MVPMatrix * a_position;\n"
        "v_texCoord = a_texCoord;\n"
        "}"
};

static char fShaderStr[] =
        "#version 300 es\n"
        "precision highp float;\n"
        "in vec2 v_texCoord;\n"
        "layout(location = 0) out vec4 outColor;\n"
        "uniform sampler2D s_texture0;\n"
        "uniform sampler2D s_texture1;\n"
        "uniform sampler2D s_texture2;\n"
        "uniform int u_nImgType;// 1:RGBA, 2:NV21, 3:NV12, 4:I420\n"
        "\n"
        "void main()\n"
        "{\n"
        "\n"
        "    if(u_nImgType == 1) //RGBA\n"
        "    {\n"
        "        outColor = texture(s_texture0, v_texCoord);\n"
        "    }\n"
        "    else if(u_nImgType == 2) //NV21\n"
        "    {\n"
        "        vec3 yuv;\n"
        "        yuv.x = texture(s_texture0, v_texCoord).r;\n"
        "        yuv.y = texture(s_texture1, v_texCoord).a - 0.5;\n"
        "        yuv.z = texture(s_texture1, v_texCoord).r - 0.5;\n"
        "        highp vec3 rgb = mat3(1.0,       1.0,     1.0,\n"
        "        0.0, \t-0.344, \t1.770,\n"
        "        1.403,  -0.714,     0.0) * yuv;\n"
        "        outColor = vec4(rgb, 1.0);\n"
        "\n"
        "    }\n"
        "    else if(u_nImgType == 3) //NV12\n"
        "    {\n"
        "        vec3 yuv;\n"
        "        yuv.x = texture(s_texture0, v_texCoord).r;\n"
        "        yuv.y = texture(s_texture1, v_texCoord).r - 0.5;\n"
        "        yuv.z = texture(s_texture1, v_texCoord).a - 0.5;\n"
        "        highp vec3 rgb = mat3(1.0,       1.0,     1.0,\n"
        "        0.0, \t-0.344, \t1.770,\n"
        "        1.403,  -0.714,     0.0) * yuv;\n"
        "        outColor = vec4(rgb, 1.0);\n"
        "    }\n"
        "    else if(u_nImgType == 4) //I420\n"
        "    {\n"
        "        vec3 yuv;\n"
        "        yuv.x = texture(s_texture0, v_texCoord).r;\n"
        "        yuv.y = texture(s_texture1, v_texCoord).r - 0.5;\n"
        "        yuv.z = texture(s_texture2, v_texCoord).r - 0.5;\n"
        "        highp vec3 rgb = mat3(1.0,       1.0,     1.0,\n"
        "                              0.0, \t-0.344, \t1.770,\n"
        "                              1.403,  -0.714,     0.0) * yuv;\n"
        "        outColor = vec4(rgb, 1.0);\n"
        "    }\n"
        "    else\n"
        "    {\n"
        "        outColor = vec4(1.0);\n"
        "    }\n"
        "}";

static char fMeshShaderStr[] =
        "//dynimic mesh 动态网格\n"
        "#version 300 es\n"
        "precision highp float;\n"
        "in vec2 v_texCoord;\n"
        "layout(location = 0) out vec4 outColor;\n"
        "uniform sampler2D s_TextureMap;//采样器\n"
        "uniform float u_Offset;\n"
        "uniform vec2 u_TexSize;\n"
        "void main()\n"
        "{\n"
        "    vec2 imgTexCoord = v_texCoord * u_TexSize;\n"
        "    float sideLength = u_TexSize.y / 6.0;\n"
        "    float maxOffset = 0.15 * sideLength;\n"
        "    float x = mod(imgTexCoord.x, floor(sideLength));\n"
        "    float y = mod(imgTexCoord.y, floor(sideLength));\n"
        "\n"
        "    float offset = u_Offset * maxOffset;\n"
        "\n"
        "    if(offset <= x\n"
        "    && x <= sideLength - offset\n"
        "    && offset <= y\n"
        "    && y <= sideLength - offset)\n"
        "    {\n"
        "        outColor = texture(s_TextureMap, v_texCoord);\n"
        "    }\n"
        "    else\n"
        "    {\n"
        "        outColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
        "    }\n"
        "}";

static char fGrayShaderStr[] =
        "//黑白滤镜\n"
        "#version 300 es\n"
        "precision highp float;\n"
        "in vec2 v_texCoord;\n"
        "layout(location = 0) out vec4 outColor;\n"
        "uniform sampler2D s_TextureMap;//采样器\n"
        "void main()\n"
        "{\n"
        "    outColor = texture(s_TextureMap, v_texCoord);\n"
        "    if(v_texCoord.x > 0.5)\n"
        "        outColor = vec4(vec3(outColor.r*0.299 + outColor.g*0.587 + outColor.b*0.114), outColor.a);\n"
        "}";

GLfloat verticesCoords[] = {
        -1.0f, 1.0f, 0.0f,  // Position 0
        -1.0f, -1.0f, 0.0f,  // Position 1
        1.0f, -1.0f, 0.0f,  // Position 2
        1.0f, 1.0f, 0.0f,  // Position 3
};

GLfloat textureCoords[] = {
        0.0f, 0.0f,        // TexCoord 0
        0.0f, 1.0f,        // TexCoord 1
        1.0f, 1.0f,        // TexCoord 2
        1.0f, 0.0f         // TexCoord 3
};

GLushort indices[] = {0, 1, 2, 0, 2, 3};


void VideoGLRender::Init(int videoWidth, int height, int *dstSize) {
    dstSize[0] = videoWidth;
    dstSize[1] = height;
    mFrameIndex = -1;
}

void VideoGLRender::RenderVideoFrame(NativeImage *nativeImage) {
    if (!nativeImage || !nativeImage->ppPlane[0]) {
        LOG_E("VideoGLRender::RenderVideoFrame  nativeImage is nullptr or ppPlane is nullptr ");
        return;
    }
    unique_lock<mutex> lock(mMutex);
    if (mRenderImage.ppPlane[0] == nullptr) {
        mRenderImage.format = nativeImage->format;
        mRenderImage.width = nativeImage->width;
        mRenderImage.height = nativeImage->height;
        NativeImageUtil::AllocNativeImage(&mRenderImage);
    }
    NativeImageUtil::CopyNativeImage(nativeImage, &mRenderImage);


}

void VideoGLRender::UnInit() {

}

void VideoGLRender::OnSurfaceCreated() {
    mProgramObj = GLHelper::CreateProgram(vShaderStr, fShaderStr);
    if (!mProgramObj) {
        LOG_E("VideoGLRender CreateProgram failed ");
        return;
    }
    glGenTextures(TEXTURE_NUM, mTextureIds);

    for (int i = 0; i < TEXTURE_NUM; ++i) {
        glActiveTexture(GL_TEXTURE0 + i);
        glBindTexture(GL_TEXTURE_2D, mTextureIds[i]);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glBindTexture(GL_TEXTURE_2D, GL_NONE);
    }
    glGenBuffers(3, mVboIds);
    glBindBuffer(GL_ARRAY_BUFFER, mVboIds[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(verticesCoords), verticesCoords, GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, mVboIds[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(textureCoords), textureCoords, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVboIds[3]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glGenVertexArrays(1, &mVaoId);
    glBindVertexArray(mVaoId);

    glBindBuffer(GL_ARRAY_BUFFER, mVboIds[0]);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), nullptr);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);

    glBindBuffer(GL_ARRAY_BUFFER, mVboIds[1]);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), nullptr);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVboIds[2]);

    glBindVertexArray(GL_NONE);
    UpdateMVPMatrix(0, 0, 1.0f, 1.0f);
    mTouchXY = vec2(0.5f, 0.5f);


}

void VideoGLRender::OnSurfaceChanged(int width, int height) {
    mScreenSize[0] = width;
    mScreenSize[1] = height;
    glViewport(0, 0, width, height);
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

}

void VideoGLRender::OnDrawFrame() {

    glClear(GL_COLOR_BUFFER_BIT);
    if (mProgramObj == GL_NONE || mRenderImage.ppPlane[0] == nullptr) return;
    mFrameIndex++;
    unique_lock<mutex> lock(mMutex);
    switch (mRenderImage.format) {
        case IMAGE_FORMAT_RGBA:
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, mTextureIds[0]);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mRenderImage.width,
                         mRenderImage.height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                         mRenderImage.ppPlane[0]);
            glBindTexture(GL_TEXTURE_2D, GL_NONE);

            break;
        case IMAGE_FORMAT_NV12:
        case IMAGE_FORMAT_NV21:

            // udpate Y plane
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, mTextureIds[0]);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
                         mRenderImage.width, mRenderImage.height, 0, GL_LUMINANCE,
                         GL_UNSIGNED_BYTE, mRenderImage.ppPlane[0]);
            glBindTexture(GL_TEXTURE_2D, GL_NONE);

//            update uv plane
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D, mTextureIds[1]);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
                         mRenderImage.width >> 1, mRenderImage.height >> 1, 0,
                         GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, mRenderImage.ppPlane[1]);
            glBindTexture(GL_TEXTURE_2D, GL_NONE);

            break;
        case IMAGE_FORMAT_I420:
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, mTextureIds[1]);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
                         mRenderImage.width, mRenderImage.height, 0,
                         GL_LUMINANCE, GL_UNSIGNED_BYTE, mRenderImage.ppPlane[0]
            );
            glBindTexture(GL_TEXTURE_2D, GL_NONE);
            break;
        default:
            break;
    }
    lock.unlock();
    glUseProgram(mProgramObj);
    glBindVertexArray(mVaoId);
    GLHelper::SetMat4(mProgramObj, "u_MVPMatrix", mMVPMatrix);

    for (int i = 0; i < TEXTURE_NUM; ++i) {
        glActiveTexture(GL_TEXTURE0 + i);
        glBindTexture(GL_TEXTURE_2D, mTextureIds[i]);
        char sampleName[64] = {0};
        GLHelper::SetInt(mProgramObj, sampleName, i);
    }
    float offset = sin(mFrameIndex * MATH_PI);
    GLHelper::SetFloat(mProgramObj, "u_Offset", offset);
    GLHelper::SetVec2(mProgramObj, "u_TextSize", vec2(mRenderImage.width, mRenderImage.height));
    GLHelper::SetInt(mProgramObj, "u_nImage", mRenderImage.format);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);

}

VideoGLRender *VideoGLRender::GetInstance() {
    if (mInstance == nullptr) {
        lock_guard<mutex> lock(mMutex);
        if (mInstance == nullptr) {
            mInstance = new VideoGLRender();
        }

    }
    return mInstance;
}

void VideoGLRender::ReleaseInstance() {
    if (mInstance) {
        lock_guard<mutex> lock(mMutex);
        if (mInstance) {
            delete mInstance;
            mInstance = nullptr;
        }
    }
}


void VideoGLRender::SetTouchLoc(float touchX, float touchY) {


}

VideoGLRender::VideoGLRender() : VideoRender(VIDEO_RENDER_OPENGL) {

}

VideoGLRender::~VideoGLRender() {
    NativeImageUtil::FreeNativeImage(&mRenderImage);
}

void VideoGLRender::UpdateMVPMatrix(int angelX, int angelY, int scaleX, int scaleY) {
    angelX = angelX % 360;
    angelY = angelY % 360;

    float radiansX = static_cast<float> (MATH_PI / 180.0f * angelX);
    float radiansY = static_cast<float>(MATH_PI / 180.0f * angelY);
//投影
    mat4 projection = ortho(-1.0f, 1.0f, -1.0f, 1.0f, 0.1f, 100.0f);

    mat4 view = lookAt(
            vec3(0, 0, 4),
            vec3(0, 0, 0),
            vec3(0, 1, 0)
    );

    mat4 model = mat4(1.0f);
    model = scale(model, vec3(scaleX, scaleY, 1.0f));
    model = rotate(model, radiansX, vec3(1.0f, 0.0f, 0.0f));
    model = rotate(model, radiansY, vec3(0.0f, 1.0f, 0.0f));
    model = translate(model, vec3(0.0f, 0.0f, 0.0f));

    mMVPMatrix = projection * view * model;

}



